package k8s

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"log"
	"log/slog"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"dlm/internal/db"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/tools/remotecommand"
)

var (
	podCache         map[string][]v1.Pod  // 缓存Pod列表，key为命名空间
	podCacheTime     map[string]time.Time // 缓存时间记录
	podCacheLock     sync.Mutex           // 缓存同步锁
	podCacheDuration = 2 * time.Hour      // 缓存有效期2小时
)

// CollectStorageData 从配置文件读取命名空间，采集指定命名空间的存储数据
func CollectStorageData(clientset *kubernetes.Clientset, namespaces []string) ([]db.StorageData, error) {
	if len(namespaces) == 0 {
		return nil, fmt.Errorf("未指定命名空间")
	}
	log.Printf("采集%d个命名空间...", len(namespaces))

	var storageDataList []db.StorageData

	// 遍历命名空间采集数据
	for _, ns := range namespaces {
		pods, err := GetPods(clientset, ns)
		if err != nil {
			slog.Error("获取Pod失败", "namespace", ns, "error", err)
			continue
		}
		log.Printf("命名空间%s：发现%d个Pod", ns, len(pods))

		for _, pod := range pods {
			if pod.Status.Phase != v1.PodRunning {
				log.Printf("命名空间%s Pod%s：非运行状态（%s），跳过", ns, pod.Name, pod.Status.Phase)
				continue
			}

			appLabel := GetAppLabel(pod)

			for _, container := range pod.Spec.Containers {
				mountMap := GetVolumeMounts(pod, container.Name)
				output, err := ExecuteCommandInContainer(clientset, pod.Name, ns, container.Name)
				if err != nil {
					log.Printf("命名空间%s Pod%s 容器%s：执行命令失败：%v", ns, pod.Name, container.Name, err)
					continue
				}

				filteredLines := FilterRBDOutput(output)
				// 按行分割过滤后的输出
				lines := strings.Split(filteredLines, "\n")
				for _, line := range lines {
					if line == "" { // 跳过空行
						continue
					}
					fields := strings.Fields(line)
					if len(fields) < 5 {
						log.Printf("命名空间%s Pod%s 容器%s：行数据字段不足5个，跳过：%s", ns, pod.Name, container.Name, line)
						continue
					}
					// 标准化挂载路径（移除尾部斜杠）
					mountPath := strings.TrimSuffix(fields[4], "/")
					pvcName := mountMap[mountPath]
					if pvcName == "" { // 修复变量拼写错误 pvcname -> pvcName
						log.Printf("警告：命名空间%s Pod%s 容器%s，未找到挂载路径[%s]对应的PVC", ns, pod.Name, container.Name, mountPath)
					}
					data := db.StorageData{
						Namespace:     ns,
						App:           appLabel,
						PVCName:       pvcName,
						TotalSize:     fields[0],
						UsedSize:      fields[1],
						AvailableSize: fields[2],
						UsagePercent:  fields[3],
					}
					storageDataList = append(storageDataList, data)
				}
			}
		}
	}

	log.Printf("采集完成，共获取%d条数据", len(storageDataList))
	return storageDataList, nil
}

// getNamespacesFromFile 从配置文件读取命名空间（每行一个）
func getNamespacesFromFile() ([]string, error) {
	// 配置文件路径：项目根目录/configs/namespaces.txt
	path := filepath.Join("configs", "namespaces.txt")
	file, err := os.Open(path)
	if err != nil {
		return nil, fmt.Errorf("打开文件失败（路径：%s）：%v", path, err)
	}
	defer file.Close()

	var namespaces []string
	scanner := bufio.NewScanner(file)
	lineNum := 0
	for scanner.Scan() {
		lineNum++
		ns := strings.TrimSpace(scanner.Text())
		if ns == "" {
			continue // 跳过空行
		}
		// 简单校验命名空间格式（只能包含字母、数字、-和.）
		if !strings.ContainsAny(ns, "abcdefghijklmnopqrstuvwxyz0123456789-._") {
			slog.Warn("无效的命名空间格式", "line", lineNum, "namespace", ns)
			continue
		}
		namespaces = append(namespaces, ns)
	}

	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("读取文件内容失败：%v", err)
	}
	return namespaces, nil
}

// FilterRBDOutput 严格筛选行首为/dev/rbd的行，并提取存储字段
func FilterRBDOutput(output string) string {
	var filtered bytes.Buffer
	lines := strings.Split(output, "\n")

	for _, line := range lines {
		trimmedLine := strings.TrimSpace(line)
		if strings.HasPrefix(trimmedLine, "/dev/rbd") {
			fields := strings.Fields(trimmedLine)
			// 确保至少有7个字段（设备、类型、总大小、已用、可用、使用率、挂载路径）
			if len(fields) >= 7 {
				// 写入总大小、已用、可用、使用率、挂载路径（修正字段索引）
				_, err := filtered.WriteString(fmt.Sprintf("%s %s %s %s %s\n",
					fields[2], fields[3], fields[4], fields[5], fields[6]))
				if err != nil {
					log.Printf("写入缓冲区失败：%v", err)
				}
			} else {
				log.Printf("行首匹配/dev/rbd但字段不足7个：%s（字段数：%d）", trimmedLine, len(fields))
			}
		}
	}
	return filtered.String()
}

// GetPods 获取指定命名空间的Pod列表（带缓存）
func GetPods(clientset *kubernetes.Clientset, namespace string) ([]v1.Pod, error) {
	podCacheLock.Lock()
	defer podCacheLock.Unlock()

	if podCache == nil {
		podCache = make(map[string][]v1.Pod)
		podCacheTime = make(map[string]time.Time)
	}

	// 检查缓存是否有效（处理未初始化的命名空间缓存时间）
	if t, ok := podCacheTime[namespace]; ok && time.Since(t) < podCacheDuration {
		return podCache[namespace], nil
	}

	// 缓存失效，重新从API获取
	pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		return nil, err
	}

	// 更新缓存
	podCache[namespace] = pods.Items
	podCacheTime[namespace] = time.Now()
	return pods.Items, nil
}

// ExecuteCommandInContainer 在容器中执行df -hT命令
func ExecuteCommandInContainer(clientset *kubernetes.Clientset, podName, ns, containerName string) (string, error) {
	req := clientset.CoreV1().RESTClient().Post().
		Resource("pods").
		Name(podName).
		Namespace(ns).
		SubResource("exec")

	parameterCodec := runtime.NewParameterCodec(scheme.Scheme)
	req.VersionedParams(&v1.PodExecOptions{
		Command:   []string{"/bin/sh", "-c", "df -hT"},
		Container: containerName,
		Stdout:    true,
		Stderr:    true,
		TTY:       false,
	}, parameterCodec)

	config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
	if err != nil {
		return "", err
	}

	exec, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL())
	if err != nil {
		return "", err
	}

	var stdout, stderr bytes.Buffer
	err = exec.Stream(remotecommand.StreamOptions{
		Stdout: &stdout,
		Stderr: &stderr,
	})
	if err != nil {
		return "", fmt.Errorf("命令执行失败：%v，错误输出：%s", err, stderr.String())
	}

	return stdout.String(), nil
}

// GetAppLabel 提取应用标签（优先从标签获取，否则用Pod名称）
func GetAppLabel(pod v1.Pod) string {
	if app := pod.Labels["app"]; app != "" {
		return app
	}
	if app := pod.Labels["application"]; app != "" {
		return app
	}
	if app := pod.Labels["app.kubernetes.io/name"]; app != "" {
		return app
	}
	return pod.Name
}

// GetVolumeMounts 获取容器的挂载路径与PVC名称的映射
func GetVolumeMounts(pod v1.Pod, containerName string) map[string]string {
	mountMap := make(map[string]string)

	// 1. 先建立volume名称到PVC的映射
	volumeToPVC := make(map[string]string)
	for _, vol := range pod.Spec.Volumes {
		if vol.PersistentVolumeClaim != nil {
			volumeToPVC[vol.Name] = vol.PersistentVolumeClaim.ClaimName
		}
	}

	// 2. 再建立挂载路径到volume名称的映射（标准化路径）
	for _, container := range pod.Spec.Containers {
		if container.Name != containerName {
			continue
		}
		for _, mount := range container.VolumeMounts {
			// 标准化挂载路径（移除尾部斜杠）
			normalizedPath := strings.TrimSuffix(mount.MountPath, "/")
			if pvcName, ok := volumeToPVC[mount.Name]; ok {
				// 同时存储原始路径和标准化路径，提高匹配成功率
				mountMap[mount.MountPath] = pvcName
				if normalizedPath != mount.MountPath {
					mountMap[normalizedPath] = pvcName
				}
			}
		}
	}
	return mountMap
}
